/*
 * SemanticAnalyzer.cpp
 *
 *  Created on: 14 июн. 2022 г.
 *      Author: nikitas
 */

#include "SemanticAnalyzer.h"

namespace STC_calc
{

const std::map<std::string, std::pair<std::function<void(string_list)>, size_t>> SemanticAnalyzer::params_map
{
    {"--SIMPLE", {&Arguments::arg_SIMPLE, 0}},
    {"--DIFF", {&Arguments::arg_DIFF, 0}}
};

bool SemanticAnalyzer::can_convert(const std::string& str) noexcept
{
	bool was_point = false;
	bool was_minus = false;
	for (auto s : str)
	{
		if (was_point && (s == '.')) return false;
		if (was_minus && (s == '-')) return false;

		if (s == '.')
		{
			was_point = true;
			continue;
		}

		if (s == '-')//probably will be a runtime error if something like '3-3' will be get.
		{
			was_minus = true;
			continue;
		}

		if (s == '0') continue;
		if (strtol(&s, nullptr, 10) == 0) return false;
	}
	return true;
}

std::string SemanticAnalyzer::comma_fix(const std::string& str) noexcept
{//transform ',' to '.' to prevent errors if somebody use commas
	std::string ret = "";
	for (char s : str)
	{
		if (s == ',') s = '.';
		ret += s;
	}
	return ret;
}

void SemanticAnalyzer::check_convert(std::string& str)
{
	auto ret = try_convert(str);
	if (ret.second)
	{
		str = ret.first;
		return;
	}
	const std::string err_log = "Error: " + str + " can't be converted into long double";
	throw RuntimeErrorClass(err_log, Enums::convertation);
}

std::pair<std::string, string_list> SemanticAnalyzer::cut_subfunction(const std::string open_bracket, const string_list& args)
{
    std::string cl_bracket, ret_str;
    string_list ret_list = args;

    size_t br_count = 1;
    if (open_bracket == "{") cl_bracket = "}";
    else cl_bracket = ")";


    while (br_count != 0)
    {
        if (ret_list.empty()) throw(RuntimeErrorClass("Error: invalid arguments"));
        std::string iterator = *ret_list.begin();
        for (int i = 0; i<iterator.length(); ++i)
        {
            if (iterator.substr(i) == open_bracket) ++br_count;
            if (iterator.substr(i) == cl_bracket) --br_count;
        }
        ret_str+=iterator + " ";
        ret_list.pop_front();
        if (br_count < 0) throw(RuntimeErrorClass("Error: invalid arguments"));
    }

    return {ret_str, ret_list};
}

std::pair<std::string, string_list> SemanticAnalyzer::define_subfunction(const string_list& args)
{
	std::string ret_str = "";
	string_list ret_list = args;
    if (args.begin()->substr(0,1) == "{" || args.begin()->substr(0,1) == "(")
    {
        return cut_subfunction(args.begin()->substr(0,1), ret_list);
    }
    ret_str+=*args.begin();
    ret_list.pop_front();
    return {ret_str, ret_list};

}

std::pair<std::string, bool> SemanticAnalyzer::try_convert(const std::string &str) noexcept
{
	std::string ret = str;
	if (can_convert(ret)) return {ret, true};
	ret = comma_fix(ret);
	if (can_convert(ret)) return {ret, true};
	return {"", false};
}

ldouble_vector SemanticAnalyzer::convert(const string_vector& args)
{
	ldouble_vector result;
	for (auto ptr : args)
	{
		check_convert(ptr);
		long double iter = strtold(ptr.c_str(), NULL);
		result.push_back(iter);
	}
	return result;
}

string_list SemanticAnalyzer::make_main_parameters_safe(const int argc, char* argv[])
{
	string_list ret;
	for (int i = 0; i < argc; i++)
	{
		std::string str = *argv;
		argv++;
		ret.push_back(str);
	}
	return ret;
}


std::string SemanticAnalyzer::careful_remove_spaces(std::string func)
{
    size_t num_of_brackets = 0;
    for (int i = 0; i < func.length(); ++i)
    {
        std::string substr = func.substr(i,1);

        if ((num_of_brackets == 0)&&(substr == " ")) func.erase(i);
        else if (substr == "{") ++num_of_brackets;
        else if (substr == "}") --num_of_brackets;
    }
    return func;
}
static bool SemanticAnalyzer::is_short_params(const std::string& arg)
{
    if (arg.substr(0,1) != "-") return false;
    for (size_t i = 1; i < arg.length; ++i)
    {
        auto param = params_map.find(arg.substr(i, 1));
        if (param == params_map.end()) return false;
        if ((param->second.second != 0)&&(i != arg.length()-1)) throw (RuntimeErrorClass("invalid arguments: -" + arg.substr(i,1) + " should have at least 1 add. parameter" ));
    }
    return true;
}
string_list SemanticAnalyzer::make_simple_params(const std::string& arg, string_list arguments)
{
    for (size_t i = 1; i < arg.length(); ++i) (params_map.find(arg.substr(i, 1)))->second.first(arguments);
    return arguments;
}
static bool SemanticAnalyzer::is_long_param(const std::string& arg)
{
    return params_map.find(arg) != params_map.end();
}
static string_list SemanticAnalyzer::make_param(const std::string& arg, string_list arguments)
{
    (params_map.find(arg))->second.first(arguments);
    return arguments;
}

string_list SemanticAnalyzer::analyze_component(string_list arguments)
{
    std::string arg = arguments.front();
    arguments.pop_front();

    if (is_short_params(arg))
    {
        arguments = make_simple_params(arg, arguments);
        return arguments;
    }
    if (is_long_param(arg))
    {
        arguments = make_param(arg, arguments);
        return arguments;
    }
    std::string func = arg;
    while (!arguments.empty())
    {
        func += arguments.front() + " ";
        arguments.pop_front();
    }
    func = careful_remove_spaces(func);
}
void SemanticAnalyzer::cyclic_analyze(const string_list& arguments)
{
    string_list args_list = arguments;
    while (!args_list.empty()) arg_list = analyze_component(args_list);

}

string_list SemanticAnalyzer::clop_parameters(const string_list& arguments)
{
	string_list n_v(arguments.size());
	std::copy(arguments.begin(), arguments.end(), n_v.begin());
	n_v.pop_front();

    return n_v;
}

std::shared_ptr<CurrentOptions> SemanticAnalyzer::make_analysis(const int argc, char* argv[])
{
    Config::next_configs();
	string_list parameters_list = make_main_parameters_safe(argc, argv);
	Config::set_location(*parameters_list.begin());
	parameters_list.pop_front();
    cyclic_analyze(parameters_list);
}

} /* namespace STC_calc */
